#ifndef CPATTERN_H
#define CPATTERN_H

#include <iostream>
#include <vector>
#include <string>
#include <math.h>
using namespace std;

template <typename _TO> class CPattern {
public:
	vector<_TO>		_features;
	string			_label;

public:
	
	CPattern (int _nf = 0){
		_label = "None";
		for (int i = 0; i < _nf; i++)
			_features.push_back(NULL);
	}

	_TO& operator[] (int pos){
		return _features[pos];
	}

	void operator << (const string& _l) {
		_label = _l;
	}

	CPattern& operator << (const _TO& _f) {
		__push_back(_f);
		return *this;
	}

	double operator () (void){
		double _average = 0.0;
		for (int i = 0; i < (int)_features.size(); i++)
			_average +=  _features[i];
		_average /= _features.size();
		return _average;
	}

	bool operator < (const CPattern& _p){
		return norma() < _p.norma();
	}

	bool operator <= (const CPattern& _p){
		return norma() <= _p.norma();
	}

	bool operator > (const CPattern& _p){
		return norma() > _p.norma();
	}

	bool operator >= (const CPattern& _p){
		return norma() >= _p.norma();
	}

	bool operator == (const CPattern& _p){
		return norma() == _p.norma();
	}
	
	bool operator != (const CPattern& _p){
		return norma() != _p.norma();
	}

	CPattern operator + (const double& _f){
		CPattern _p;
		for (int i = 0; i < (int)_features.size(); i++)
			_p << (_features[i] + _f);
		return _p;
	}

	CPattern operator + (const CPattern& _p){
		CPattern _ptemp;
		for (int i = 0; i < (int)_p._features.size(); i++)
			_ptemp <<(_features[i] + _p._features[i]);
		return _ptemp;
	}

	CPattern& operator += (const double& _f){
		for (int i = 0; i < (int)_features.size(); i++)
			_features[i] += _f;
		return *this;
	}

	CPattern& operator += (const CPattern& _p){
		for (int i = 0; i < (int)_p._features.size(); i++)
			_features[i] += _p._features[i];
		return *this;
	}


	CPattern& operator *= (int _f){ // Producto escalar
		for (int i = 0; i < (int)_features.size(); i++)
			_features[i] *= _f;
		return *this;
	}

	CPattern operator * (double _f){ // Producto escalar
		CPattern _ptemp;
		for (int i = 0; i < (int)_features.size(); i++)
			_ptemp << _features[i] * _f;
		return _ptemp;
	}

	CPattern& operator /= (int _f){ 
	// Division escalar
		for (int i = 0; i < (int)_features.size(); i++)
			_features[i] /= _f;
		return *this;
	}

	CPattern operator / (int _f){ 
	// Division escalar
		CPattern _ptemp;
		for (int i = 0; i < (int)_features.size(); i++)
			_ptemp <<  _features[i] / _f;
		return _ptemp;
	}

	CPattern normalization (double _n) {
		CPattern _ptemp;
		for (int i = 0; i < (int)_features.size(); i++)
			_ptemp << _n / _features[i];
		return _ptemp;
	}

	long double distancia_euclideana (const CPattern& _p){ 
	// distancia euclideana
		long double dist = 0.0;
		for (int i = 0; i < (int)_p._features.size(); i++)
			dist += (_features[i] - _p._features[i])*(_features[i] - _p._features[i]);
		return sqrt(dist);
	}

	CPattern operator - (const CPattern& _p){ 
	// distancia vectorial	
		CPattern _ptemp; 
		for (int i = 0; i < (int)_p._features.size(); i++)
			_ptemp << (_p._features[i] - _features[i]);
		return _ptemp;
	}

	double norma (void){ 
	// norma del vector
		double _norm = NULL;
		for (int i = 0; i < (int)_features.size(); i++){
			_norm += _features[i] * _features[i];
		}
		return sqrt(_norm); 
	}

	int size (void){
	// Tamanho del vector
		return (int)_features.size();
	}
	
	template <typename __TO>
	friend  ostream& operator << (ostream& out, const CPattern <__TO>& p){
		out << "[ ";
		for (int i = 0; i  < (int)p._features.size(); i++)
			out << p._features[i] << " ";
		out << "]";
		out << " L: " << p._label;
		return out;
	}

private:

	void __push_back(const _TO& _f){
		_features.push_back(_f);
	}

};

#endif